﻿using System;
using System.Collections;
using System.Collections.Generic;
using Gameplay.PVE.Survivor;
using Gameplay.PVE.Utils;
using OWL.Rendering.HRP;
using UnityEngine;
using UnityEngine.Profiling;
using Yoozoo.HRP.Runtime;

public class MapFogUtility
{
    //根据点，生成凸多边形
    public static Mesh CreateMeshByPoints(List<Vector3> points,Vector3 centerPoint)
    {
        var radius = 0.5f;
        var mesh = new Mesh();
        var vertices = new List<Vector3>();
        var triangles = new List<int>();
        var uvs = new List<Vector2>();
        var colors = new List<Color>();
        var index = 0;
        var triangles2 = new List<int>();
        var directionAngle = 0f;
        mesh.subMeshCount = 2;
        /*var centerPoint = Vector3.zero;
        for (int i = 0; i < points.Count; i++)
        {
            centerPoint += points[i];
        }
        centerPoint /= points.Count;*/
        var extraPoints = new List<Vector3>();
        for (int i = 0; i < points.Count; i++)
        {
            var lastPoint = points[(i - 1 + points.Count) % points.Count];
            var point = points[i];
            var nextPoint = points[(i + 1) % points.Count];
            vertices.Add(point);
            vertices.Add(nextPoint);
            vertices.Add(centerPoint);
            uvs.Add(new Vector2(point.x, point.z));
            uvs.Add(new Vector2(nextPoint.x, nextPoint.z));
            uvs.Add(new Vector2(centerPoint.x, centerPoint.z));
            colors.Add(new Color(0,0,0,1));
            colors.Add(new Color(0,0,0,1));
            colors.Add(new Color(0,0,0,1));
            var centerPointIndex = index;
            triangles.Add(index + 2);
            triangles.Add(index + 1);
            triangles.Add(index);
            index+=3;
            //外围
            var arcStartForward = (point - lastPoint).normalized;
            var arcEndForward = (point - nextPoint).normalized;
            var isInside = false;
            var currentAngle = PveUtils.GetAngle(point - lastPoint, nextPoint - point);
            if (directionAngle == 0)
            {
                
            }
            else
            {
                isInside = currentAngle * directionAngle < 0;
            }
            if (isInside)
            {
                var extra1 = point - arcStartForward * radius;
                var extra2 = point - arcEndForward * radius;
                extraPoints.Add((extra1 + extra2)/2);
                extraPoints.Add(point);
                extraPoints.Add((extra1 + extra2)/2);
            }
            else
            {
                directionAngle = currentAngle;
                var angle = PveUtils.GetAngle(arcStartForward, arcEndForward);
                var sampleCount = 10;
                var angleStep = angle / sampleCount;
                for (int j = 0; j < sampleCount; j++)
                {
                    var newPoint = point + Quaternion.AngleAxis(j * angleStep, Vector3.up) * arcStartForward * radius;
                    var newNextPoint = point + Quaternion.AngleAxis((j + 1) * angleStep, Vector3.up) * arcStartForward * radius;
                    vertices.Add(newPoint);
                    vertices.Add(newNextPoint);
                    vertices.Add(point);
                    uvs.Add(new Vector2(newPoint.x, newPoint.z));
                    uvs.Add(new Vector2(newNextPoint.x, newNextPoint.z));
                    uvs.Add(new Vector2(point.x, point.z));
                    colors.Add(new Color(0,0,0,0));
                    colors.Add(new Color(0,0,0,0));
                    colors.Add(new Color(0,0,0,1));
                    triangles2.Add(index + 2);
                    triangles2.Add(index + 1);
                    triangles2.Add(index);
                    index+=3;
                }
                var extra1 = point + arcStartForward * radius;
                var extra2 = point + arcEndForward * radius;
                extraPoints.Add(extra2);
                extraPoints.Add(point);
                extraPoints.Add(extra1);
            }
        }

        for (int i = 0; i < extraPoints.Count / 3; i++)
        {
            var p1 = extraPoints[i * 3 + 1];
            var p2 = extraPoints[i * 3 + 2];
            var p3 = extraPoints[(i * 3 + 3) % extraPoints.Count];
            var p4= extraPoints[(i * 3 + 4) % extraPoints.Count];
            vertices.Add(p1);
            vertices.Add(p2);
            vertices.Add(p3);
            vertices.Add(p4);
            uvs.Add(new Vector2(p1.x, p1.z));
            uvs.Add(new Vector2(p2.x, p2.z));
            uvs.Add(new Vector2(p3.x, p3.z));
            uvs.Add(new Vector2(p4.x, p4.z));
            colors.Add(new Color(0,0,0,1));
            colors.Add(new Color(0,0,0,0));
            colors.Add(new Color(0,0,0,0));
            colors.Add(new Color(0,0,0,1));
            triangles2.Add(index);
            triangles2.Add(index + 1);
            triangles2.Add(index + 3);
            triangles2.Add(index + 1);
            triangles2.Add(index + 2);
            triangles2.Add(index + 3);
            index += 4;
        }
        mesh.SetVertices(vertices);
        mesh.SetTriangles(triangles, 0);
        mesh.SetTriangles(triangles2, 1);
        mesh.SetUVs(0, uvs);
        mesh.SetColors(colors);
        return mesh;
    }

    public static int textureWidth = 128;
    public static int textureHeight = 128;
    
    public static Texture2D CreateTexture(List<Vector3> points,List<Vector3> cullPoints, float mapWidth,float mapHeight,float alpha)
    {
        Profiler.BeginSample("MapFogData.CreateTexture.Create");
        for (int i = 0; i < points.Count; i++)
        {
            points[i] = new Vector3(points[i].x,0,points[i].z);
        }
        var texture2d = new Texture2D(textureWidth,textureHeight);
        Profiler.EndSample();
        Profiler.BeginSample("MapFogData.CreateTexture.Assign");
        for (int i = 0; i < textureWidth; i++)
        {
            for (int j = 0; j < textureHeight; j++)
            {
                var worldX = GetWorldXByTextureX(mapWidth,textureWidth,i);
                var worldY = GetWorldYByTextureY(mapHeight,textureHeight,j);
                if (CheckPointIsInPolygon(points,new Vector3(worldX,0,worldY)) && !CheckPointIsInPolygon(cullPoints,new Vector3(worldX,0,worldY)))
                {
                    texture2d.SetPixel(i,j,new Color(0,0,0,alpha));
                }
                else
                {
                    texture2d.SetPixel(i,j,new Color(0,0,0,0));
                }
            }
        }
        Profiler.EndSample();
        Profiler.BeginSample("MapFogData.CreateTexture.Apply");
        texture2d.Apply();
        Profiler.EndSample();
        return texture2d;
    }
    
    public static float GetWorldXByTextureX(float mapWidth,float textureWidth,int x)
    {
        var worldX = (x / textureWidth * mapWidth - mapWidth / 2);
        return worldX;
    }
    
    public static float GetWorldYByTextureY(float mapHeight,float textureHeight,int y)
    {
        var worldY = (y / textureHeight * mapHeight - mapHeight / 2);
        return worldY;
    }
    
    private static int GetTextureX(Vector3 point,float mapWidth,float textureWidth)
    {
        var x = (int) ((point.x + mapWidth / 2) / mapWidth * textureWidth);
        return x;
    }
    private static int GetTextureY(Vector3 point,float mapHeight,float textureHeight)
    {
        var y = (int) ((point.z + mapHeight / 2) / mapHeight * textureHeight);
        return y;
    }

    //如果再外部，则直接返回0
    //如果在内部，则返回最近的距离
    public static float GetDistanceToPolygonEdge(List<Vector3> points,Vector3 testPoint,ref Vector3 nearestPoint1,ref Vector3 nearestPoint2)
    {
        if (!CheckPointIsInPolygon(points, testPoint))
        {
            return 0;
        }
        else
        {
            var min = float.MaxValue;
            Profiler.BeginSample("CalculatePointDistance");
            for (int i = 0; i < points.Count; i++)
            {
                var p1 = points[i];
                var p2 = points[(i + 1) % points.Count];
                var distance = BattleHelper.DistanceFromPoint2Line(testPoint, p1, p2);
                if (distance < min)
                {
                    min = distance;
                    nearestPoint1 = p1;
                    nearestPoint2 = p2;
                }
            }
            Profiler.EndSample();
            return min;
        }
    }
    public static bool CheckPointIsInPolygon(List<Vector3> points,Vector3 testPoint)
    {
        Profiler.BeginSample("MapFogData.CreateTexture.CheckPointIsInPolygon");
        if (points.Count < 3)
        {
            Profiler.EndSample();
            return false;
        }

        float raycastLen = 10000f;
        Vector3 comparePoint = (points[0] + points[1]) * 0.5f;
        // 此处一定要这样写表示射线，不然comparePoint在边上计算会有误差
        comparePoint += (comparePoint - testPoint).normalized * raycastLen;
        //Gizmos.DrawLine(testPoint, comparePoint);

        int count = 0;

        for (int i = 0; i < points.Count; ++i)
        {
            Vector3 a = points[i];
            Vector3 b = points[(i + 1) % points.Count];
            // 循环判断每条边与testPoint射向comparePoint的射线是否有交点
            if (IsIntersection(a, b, testPoint, comparePoint))
            {
                ++count;
            }
        }
        Profiler.EndSample();
        if (count % 2 == 1) return true;

        return false;
    }

    private static bool IsIntersection(Vector3 a, Vector3 b, Vector3 originPoint, Vector3 comparePoint)
    {
        // 判断是否同向
        float crossA = Mathf.Sign(Vector3.Cross(comparePoint - originPoint, a - originPoint).y);
        float crossB = Mathf.Sign(Vector3.Cross(comparePoint - originPoint, b - originPoint).y);

        if (Mathf.Approximately(crossA, crossB)) return false;

        float crossC = Mathf.Sign(Vector3.Cross(b - a, originPoint - a).y);
        float crossD = Mathf.Sign(Vector3.Cross(b - a, comparePoint - a).y);

        if (Mathf.Approximately(crossC, crossD)) return false;

        return true;
    }
}